<template>
  <div class="flex rounded overflow-hidden w-full h-full">
    <div class="w-60 bg-eee px-4 py-6 border-r border-gray-300">
      <div id="stencil" class="w-full h-full relative"></div>
    </div>
    <div class="flex-1 h-full flex flex-col">
      <div class="bg-eee px-4 py-2 flex border-b border-gray-300 justify-between items-center">
        <a-space>
          <div class="px-3 py-1.5 rounded cursor-pointer hover:bg-white">项目：
            <a-tag color="magenta">{{ projectInfo.name }}</a-tag>
          </div>
          <div class="px-3 py-1.5 rounded cursor-pointer hover:bg-white"
               @click="$modal.info({ title:'版本信息', content:versionInfo.introduce })">当前版本号：
            <a-tag color="purple">{{ versionInfo.name }}</a-tag>
          </div>
          <div class="flex">
            <div v-for="(v, i) in toolArr" :key="i">
              <div class="px-8 py-1.5 rounded cursor-pointer hover:bg-white">{{ v.title }}</div>
              <div v-if="v.child" class="absolute">
                <!--                <div v-for="(s, j) in v.child" :key="j"-->
                <!--                     class="px-8 py-1.5 rounded cursor-pointer hover:bg-white">{{ s }}-->
                <!--                </div>-->
              </div>
            </div>
          </div>
        </a-space>
        <a-space>
          <AButton class="w-20" type="primary" @click="back">返回</AButton>
          <AButton class="w-30" type="primary" @click="previewFlow">预览Flow流程</AButton>
          <AButton class="w-30" type="primary" @click="initParamModalVisible=true">置初始参数</AButton>
          <AButton class="w-20" type="primary" @click="saveNode">保存</AButton>
        </a-space>
      </div>
      <div class="flex w-full flex-1 relative">
        <div id="graph-container" class="h-full w-5/6"></div>
        <div class="w-1/6 border-l h-full border-gray-300 p-3 border-r">
          <div v-show="node && !node.data.parent" class="grap_home">
            <div class="mb-2">
              <div class="mb-1">名称：</div>
              <AInput v-model="nodeName" @change="changeData(1)"/>
            </div>
            <div class="mb-2" v-for="(v, i) in form" :key="i">
              <div class="mb-1">{{ v.properties.desc }}：</div>
              <AInput v-if="v.properties.type === 'text'" v-model="attrs[v.varName]" @change="changeData(2)"/>
              <ARadioGroup v-if="v.properties.type === 'bool'" v-model="attrs[v.varName]" @change="changeData(2)">
                <ARadio :value="true">是</ARadio>
                <ARadio :value="false">否</ARadio>
              </ARadioGroup>
              <ADatePicker
                  v-if="v.properties.type === 'date'"
                  v-model="attrs[v.varName]"
              />
            </div>
          </div>
        </div>
        <div v-show="isDel" class="bg-white shadow rounded px-8 py-2 absolute cursor-pointer text-xs"
             :style="{top: delT + 'px', left: delL + 'px'}" @click="deleteNode">
          删除
        </div>
      </div>
    </div>
  </div>
  <a-modal v-model:visible="initParamModalVisible"
           title="初始化预置参数"
           @open="getVersionInfo"
           @before-ok="updateVersion">
    <JsonEditorVue class="editor"
                   v-model="versionInfo.initParam"
                   language="zh-CN"
                   ref="editor"
    />
  </a-modal>
</template>

<script setup>
import {_} from '@/http'
import {Graph, Shape} from '@antv/x6'
import {Stencil} from '@antv/x6-plugin-stencil' // 如果使用 stencil 功能，需要安装此包
import {Transform} from '@antv/x6-plugin-transform' // 如果使用图形变换功能，需要安装此包
import {Selection} from '@antv/x6-plugin-selection' // 如果使用框选功能，需要安装此包
import {Snapline} from '@antv/x6-plugin-snapline' // 如果使用对齐线功能，需要安装此包
import {Keyboard} from '@antv/x6-plugin-keyboard' // 如果使用快捷键功能，需要安装此包
import {Clipboard} from '@antv/x6-plugin-clipboard' // 如果使用剪切板功能，需要安装此包
import {History} from '@antv/x6-plugin-history' // 如果使用撤销重做功能，需要安装此包
import {h, onMounted, ref} from "vue";
import router from "@/router/index.js";
import {useRoute} from "vue-router";
import {Message, Modal} from "@arco-design/web-vue";
import JsonEditorVue from 'json-editor-vue3'


const toolArr = [
  {
    title: '操作',
    child: ['保存', '发布版本']
  },
  {
    title: '引用'
  }
]
const common = ref([])
const if_com = ref([])
const switch_com = ref([])
const graph = ref({})
const stencil = ref({})
const delT = ref(0)
const delL = ref(0)
const isDel = ref(false)
const node = ref(null)
const attrs = ref({})
const nodeName = ref('')
const form = ref([])
// 规则编排图编号
let chartCode = ref('')
// 项目编号
let projectCode = ref('')
// 版本编号
let versionCode = ref('')
let projectInfo = ref({
  name: '',
})
let versionInfo = ref({
  name: '',
  introduce: '',
  initParam: ''
})
// 预览FLow流程内容
let previewFlowContent = ref('')
// 规则初始化参数弹窗是否可见
let initParamModalVisible = ref(false)
let editor = ref()
// 获取路由参数
const route = useRoute()
chartCode.value = route.query.chartCode
projectCode.value = route.query.projectCode
versionCode.value = route.query.versionCode

onMounted(() => {
  // 初始化项目信息
  getProjectInfo()
  // 初始化版本信息
  getVersionInfo()
  // 初始化左侧组件
  getLeft()
  // 初始化数据
  initData()
})


/**
 * 根据项目code获取项目信息
 */
async function getProjectInfo() {
  await _.project.join(`/${projectCode.value}`).get().then(({data}) => {
    console.log(data)
    projectInfo.value.name = data?.data?.projectName
    return true
  }).catch((err) => {
    console.log(err)
    // Message.error(err.response.data.message)
    Message.error('获取项目信息失败')
    return false
  })
}

/**
 * 根据版本code获取版本信息
 * @returns {Promise<void>}
 */
async function getVersionInfo() {
  await _.version.join(`/${versionCode.value}`).get().then(({data}) => {
    console.log(data)
    versionInfo.value.name = data?.data?.name
    versionInfo.value.introduce = data?.data?.introduce
    versionInfo.value.initParam = data?.data?.initParam
    if (versionInfo.value.initParam !== undefined && versionInfo.value.initParam !== '') {
      try {
        versionInfo.value.initParam = JSON.parse(versionInfo.value.initParam)
      } catch (e) {
        versionInfo.value.initParam = {}
      }
    }
    return true
  }).catch((err) => {
    console.log(err)
    // Message.error(err.response.data.message)
    Message.error('获取版本信息失败')
    return false
  })
}

/**
 * 取左侧的组件
 */
async function getLeft() {
  await _.left_component.get()
      .then(({data}) => {
        common.value = data.data.COMMON
        if_com.value = data.data.IF
        switch_com.value = data.data.SWITCH
        initGraph()
      })
}

/**
 * 初始化数据
 */
async function initData() {
  await _.flow.join(`/${chartCode.value}/${versionCode.value}/link`).get().then(({data}) => {
    console.log(data)
    if (data.code === 200) {
      const nodes = data.data.nodes
      const edges = data.data.edges
      console.log(nodes)
      console.log(edges)
      let cells = []
      edges.forEach((item) => {
        cells.push(graph.value.createEdge({
          id: item.id,
          shape: item.shape,
          source: {
            cell: item.sourceNodeId,
            port: item.sourceAnchorId
          },
          target: {
            cell: item.targetNodeId,
            port: item.targetAnchorId
          },
          attrs: item.properties?.attrs,
          zIndex: item?.properties?.zIndex ? item?.properties?.zIndex : 0,
        }))
      })
      let cmpObjToParams = {}
      common.value.forEach((v) => {
        if (v.params) {
          cmpObjToParams[v.cmpId] = v.params
        }
      })
      if_com.value.forEach((v) => {
        console.log(v)
        if (v.params) {
          cmpObjToParams[v.cmpId] = v.params
        }
      })
      switch_com.value.forEach((v) => {
        console.log(v)
        if (v.params) {
          cmpObjToParams[v.cmpId] = v.params
        }
      })

      nodes.forEach((item) => {
        console.log(item)
        const form = cmpObjToParams[item.properties.nodeId] ? JSON.parse(cmpObjToParams[item.properties.nodeId]) : null
        let obj = {}
        if (form) {
          form.forEach((v) => {
            obj[v.varName] = ''
          })
        }
        let nodeData = item?.properties?.nodeData
        if (nodeData) {
          // 移除左右两侧的单引号
          nodeData = nodeData.substring(1, nodeData.length - 1)
          // 将字符串转为对象
          nodeData = JSON.parse(nodeData)
          // 处理obj中的值
          for (let key in nodeData) {
            obj[key] = nodeData[key]
          }
        }
        cells.push(graph.value.createNode({
          id: item.id,
          shape: item.shape,
          width: item.properties?.size?.width ? item.properties?.size?.width : 66,
          height: item.properties?.size?.height ? item.properties?.size?.height : 36,
          zIndex: item.properties.zIndex,
          position: {
            "x": item.x,
            "y": item.y
          },
          label: item.text?.value,
          children: item.children,
          ports: item.properties?.ports,
          port: item.properties?.port,
          attrs: item.properties?.attrs,
          data: {
            parent: item.type === 'WhenGroupNode',
            code: item.id,
            cmpId: item.properties.nodeId,
            cmpType: item.properties.nodeType,
            params: form,
            data: obj,
            x: item.x,
            y: item.y
          }
        }))
      })
      console.log(cells)
      graph.value.resetCells(cells)
      // graph.value.zoomToFit({padding: 10, maxScale: 1})
    }
  }).catch((err) => {
    console.log(err)
    Message.error('获取数据失败，请稍后重试')
  })
}

function initGraph() {
  // #region 初始化画布
  graph.value = new Graph({
    container: document.getElementById('graph-container'),
    grid: true,
    embedding: {
      enabled: true,
      findParent({node}) {
        const bbox = node.getBBox()
        // console.log(node)
        return this.getNodes().filter((item) => {
          const data = item.getData()
          if (data && data.parent) {
            const targetBBox = item.getBBox()
            if (!node.children) {
              node.zIndex += item.zIndex
              // node.setZIndex(node.zIndex += item.zIndex)
            }
            return bbox.isIntersectWithRect(targetBBox)
          }
          return false
        })
      },
    },
    mousewheel: {
      enabled: true,
      zoomAtMousePosition: true,
      modifiers: 'ctrl',
      minScale: 0.5,
      maxScale: 3,
    },
    connecting: {
      router: 'manhattan',
      connector: {
        name: 'rounded',
        args: {
          radius: 8,
        },
      },
      anchor: 'center',
      connectionPoint: 'anchor',
      allowBlank: false,
      snap: {
        radius: 20,
      },
      createEdge() {
        return new Shape.Edge({
          attrs: {
            line: {
              stroke: '#A2B1C3',
              strokeWidth: 2,
              targetMarker: {
                name: 'block',
                width: 12,
                height: 8,
              },
            },
          },
          zIndex: 9999999,
        })
      },
      validateConnection({targetMagnet}) {
        return !!targetMagnet
      },
    },
    highlighting: {
      magnetAdsorbed: {
        name: 'stroke',
        args: {
          attrs: {
            fill: '#5F95FF',
            stroke: '#5F95FF',
          },
        },
      },
    },
  })
  // #region 使用插件
  graph.value
      .use(
          new Transform({
            resizing: true,
            rotating: true,
          }),
      )
      .use(
          new Selection({
            rubberband: true,
            showNodeSelectionBox: true,
          }),
      )
      .use(new Keyboard())
      .use(new Snapline())
      .use(new Clipboard())
      .use(new History())
  // 控制连接桩显示/隐藏
  const showPorts = (ports, show) => {
    for (let i = 0, len = ports.length; i < len; i += 1) {
      ports[i].style.visibility = show ? 'visible' : 'hidden'
    }
  }
  graph.value.on('node:mouseenter', () => {
    // 鼠标移入
    const container = document.getElementById('graph-container')
    const ports = container.querySelectorAll(
        '.x6-port-body',
    )
    showPorts(ports, true)
  })
  graph.value.on('node:mouseleave', () => {
    // 鼠标离开
    const container = document.getElementById('graph-container')
    const ports = container.querySelectorAll(
        '.x6-port-body',
    )
    showPorts(ports, false)
  })
  // 节点点击
  graph.value.on('node:click', (data) => {
    console.log(data)
    console.log(data.node.store.data.data)
    node.value = data.node
    nodeName.value = data.node.label
    form.value = data.node.store.data.data.params ? data.node.store.data.data.params : []
    attrs.value = data.node.data.data
    isDel.value = false
  })
  // 节点右键点击
  graph.value.on('cell:contextmenu', (data) => {
    delT.value = data.y
    delL.value = data.x
    node.value = data.node
    isDel.value = true
  })
  graph.value.on('edge:click', (data) => {
    isDel.value = false
  })
  graph.value.on('blank:click', (data) => {
    isDel.value = false
  })
  graph.value.on('blank:mouseenter', () => {
    // 鼠标移入
    isDel.value = false
  })

  // #region 初始化图形
  const ports = {
    groups: {
      top: {
        position: 'top',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
      right: {
        position: 'right',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
      bottom: {
        position: 'bottom',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
      left: {
        position: 'left',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
    },
    items: [
      {
        group: 'top',
      },
      {
        group: 'right',
      },
      {
        group: 'bottom',
      },
      {
        group: 'left',
      },
    ],
  }

  Graph.registerNode(
      'custom-rect',
      {
        inherit: 'rect',
        width: 66,
        height: 36,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: '#5F95FF',
            fill: '#EFF4FF',
          },
          text: {
            fontSize: 12,
            fill: '#262626',
          },
        },
        ports: {...ports}
      },
      true,
  )
  Graph.registerNode(
      'custom-polygon',
      {
        inherit: 'polygon',
        width: 66,
        height: 36,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: '#5F95FF',
            fill: '#EFF4FF',
          },
          text: {
            fontSize: 12,
            fill: '#262626',
          },
        },
        ports: {
          ...ports
        },
      },
      true,
  )

  Graph.registerNode(
      'custom-circle',
      {
        inherit: 'circle',
        width: 45,
        height: 45,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: '#5F95FF',
            fill: '#EFF4FF',
          },
          text: {
            fontSize: 12,
            fill: '#262626',
          },
        },
        ports: {...ports},
      },
      true,
  )
  initStencil()
}

function initStencil() {
  // #region 初始化 stencil
  stencil.value = new Stencil({
    title: '流程图',
    target: graph.value,
    stencilGraphWidth: 200,
    stencilGraphHeight: 180,
    collapsable: true,
    groups: [
      {
        title: '基础',
        name: 'group1',
      },
      {
        title: '判断',
        name: 'group2'
      },
      {
        title: 'Switch',
        name: 'group3'
      },
      {
        title: '群组',
        name: 'group4',
      }
    ],
    layoutOptions: {
      columns: 2,
      columnWidth: 80,
      rowHeight: 55,
    },
  })
  document.getElementById('stencil').appendChild(stencil.value.container)

  if (common.value.length > 0) {
    const commonNodes = common.value.map((item) =>
        graph.value.createNode({
          shape: 'custom-rect',
          attrs: {
            body: {
              rx: 6,
              ry: 6,
            },
          },
          label: item.defaultText,
          data: {
            parent: false,
            ...dealData(item)
          }
        })
    )
    stencil.value.load(commonNodes, 'group1')
  }
  if (if_com.value.length > 0) {
    const ifNodes = if_com.value.map((item) =>
        graph.value.createNode({
          shape: 'custom-polygon',
          attrs: {
            body: {
              refPoints: '0,10 10,0 20,10 10,20',
            },
          },
          label: item.defaultText,
          data: {
            parent: false,
            ...dealData(item)
          }
        })
    )
    stencil.value.load(ifNodes, 'group2')
  }
  if (switch_com.value.length > 0) {
    const switchNodes = switch_com.value.map((item) =>
        graph.value.createNode({
          shape: 'custom-circle',
          label: item.defaultText,
          data: {
            parent: false,
            ...dealData(item)
          }
        })
    )
    stencil.value.load(switchNodes, 'group3')
  }

  const group = graph.value.createNode({
    shape: 'custom-rect',
    label: '',
    data: {
      parent: true,
      cmpId: 'WhenGroupNode',
      cmpType: 'WHEN'
    }
  })

  stencil.value.load([group], 'group4')
}

// 处理参数
function dealData(it) {
  const form = it.params ? JSON.parse(it.params) : null
  let obj = {}
  // console.log(form)
  if (form) {
    form.forEach((v) => {
      obj[v.varName] = ''
    })
  }
  return {
    code: it.code,
    cmpId: it.cmpId,
    cmpType: it.cmpType,
    params: form,
    data: obj
  }
}

// 删除节点
function deleteNode() {
  graph.value.removeNode(node.value)
  isDel.value = false
  node.value = null
}

/**
 * 修改配置
 * @param flag
 */
function changeData(flag) {
  if (flag === 1) {
    node.value.label = nodeName.value
  } else if (flag === 2) {
    node.value.data.data = attrs.value
  }
}

/**
 * 保存节点
 */
function saveNode() {
  // 获取所有节点数据（不包含群组内的节点）
  console.log(graph.value.getEdges())
  const nodes = graph.value.getNodes()
  console.log(nodes)
  const edges = dealEdges(graph.value.getEdges())
  const nodeArr = dealNodes(nodes)
  // console.log(nodeArr)
  // console.log(edges)
  _.flow_chart.save.join(`/${chartCode.value}/${versionCode.value}`).patch({
    body: {
      nodes: nodeArr,
      edges: edges
    }
  }).then(({data}) => {
    console.log(data)
    Message.success('保存成功')
  }).catch((err) => {
    console.log(err)
    Message.error('保存失败，请稍后重试')
  })

}

function dealNodes(arr) {
  let nodes = []
  if (arr.length > 0) {
    arr.forEach((v) => {
      console.log(v)
      let d = {
        id: v.id,
        type: v.data.cmpId,
        x: v.store.data.position.x,
        y: v.store.data.position.y,
        shape: v.store.data.shape
      }
      let properties = {
        nodeType: v.data.cmpType,
        nodeId: v.data.cmpId,
        conditionNodeId: v.data.cmpId,
        zIndex: v.zIndex,
        size: v.store.data.size,
        ports: v.ports,
        port: v.port,
        attrs: v.attrs,
        shape: v.shape,
      }
      if (v.children) {
        d.children = v.children.map((s) => {
          return s.id
        })
        d.properties = properties
      } else {
        let formData = dealForm(v)
        properties.nodeData = formData === '' ? "''" : `'${formData}'`
        d.properties = properties
        d.text = {
          value: v.label
        }
      }
      nodes.push(d)
    })
  }
  return nodes
}

function dealEdges(arr) {
  let edges = []
  if (arr.length > 0) {
    arr.forEach((v) => {
      console.log('=====================')
      console.log(v)
      console.log('=====================')
      edges.push({
        id: v.id,
        type: 'polyline',
        sourceNodeId: v.source.cell,
        targetNodeId: v.target.cell,
        shape: v.store.data.shape,
        properties: {
          zIndex: v.zIndex,
          attrs: v.attrs,
        },
        sourceAnchorId: v.source.port,
        targetAnchorId: v.target.port,
      })
    })
  }
  return edges
}

function dealForm(it) {
  let obj = {}
  if (it.data.params) {
    it.data.params.forEach((v) => {
      obj[v.varName] = it.data.data[v.varName]
    })
  }
  return JSON.stringify(obj)
}


const ModalContent = {
  setup() {
    return () => h('div', {class: 'info-modal-content'}, [
      h('div', {style: 'margin-bottom: 10px;width: 100%;word-wrap: break-word;overflow-wrap: break-word;'}, `${previewFlowContent.value}`),
    ])
  },
}


/**
 * 预览Flow流程
 */
function previewFlow() {
  // 获取所有节点数据（不包含群组内的节点）
  const nodes = graph.value.getNodes()
  const edges = dealEdges(graph.value.getEdges())
  const nodeArr = dealNodes(nodes)
  // console.log(nodeArr)
  // console.log(edges)
  _.flow.join('/generateLogicFlowEL').post({
    body: {
      nodes: nodeArr,
      edges: edges
    }
  }).then(({data}) => {
    if (data.code !== 200 || data.data === '') {
      Message.error('转换失败，请稍后重试')
      return
    }
    let flowContent = data.data
    // 处理\"为\\"
    flowContent = flowContent.replace(/\"/g, '\\"')
    previewFlowContent.value = flowContent
    Modal.info({
      title: 'Flow表达式预览',
      content: () => h(ModalContent)
    });
    console.log(data)
  }).catch((err) => {
    console.log(err)
    Message.error('转换失败，请稍后重试')
  })
}

/**
 * 路由回退
 */
function back() {
  router.back()
}

/**
 * 更新版本信息
 */
async function updateVersion(done) {
  const res = await editor.value.editor.validate()
  if (res.length !== 0) {
    Message.error("JSON格式不正确，请检查！")
    done(false)
    return
  }
  _.version.join(`/${versionCode.value}`).patch({
    body: {
      initParam: JSON.stringify(versionInfo.value.initParam)
    }
  }).then(({data}) => {
    console.log(data)
    Message.success('更新成功')
    done(true)
  }).catch((err) => {
    console.log(err)
    Message.error('更新失败，请稍后重试')
    done(false)
  })
}
</script>
<style>
.info-modal-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
</style>
<style scoped>
.bg-eee {
  background: #eee;
}
</style>
